using System;
using System.Collections.Generic;
using CGF;
using LitJson;
#if UNITY_WEBGL
using System.Runtime.InteropServices;
using AOT;
#endif

//不同于 IndexedDBHelper，该类可实例化，用于存储海量记录，类似SQLite,同时也支持key-value
public class IndexedDB
{
    // 回调委托定义
    private delegate void Callback(IntPtr dbNamePtr);
    private delegate void StringCallback(IntPtr dbNamePtr, IntPtr stringPtr);
    private delegate void BinaryCallback(IntPtr dbNamePtr, IntPtr dataPtr, int size);

#if UNITY_WEBGL
    // JavaScript 插件函数声明
    [DllImport("__Internal")]
    private static extern void IndexedDB_Init(string db, int version, string storesJson, Callback finishCallback);
    [DllImport("__Internal")]
    private static extern void IndexedDB_SetKeyValue(string db, string key, string stringValue, byte[] binaryValue, int size);
    [DllImport("__Internal")]
    private static extern void IndexedDB_GetKeyValue(string db, string key, StringCallback stringCallback, BinaryCallback binaryCallback);
    [DllImport("__Internal")]
    private static extern void IndexedDB_DeleteKey(string db, string key);
    [DllImport("__Internal")]
    private static extern void IndexedDB_InsertToList(string db, string store, string columnsArrJson, string valuesArrJson);
    [DllImport("__Internal")]
    private static extern void IndexedDB_UpdateListById(string db, string store, int id, string columnsArrJson, string valuesArrJson);
    [DllImport("__Internal")]
    private static extern void IndexedDB_SelectList(string db, string store, string searchColumn, string searchValue, string searchValueType, StringCallback callback);
    [DllImport("__Internal")]
    private static extern void IndexedDB_DeleteListByColumn(string db, string store, string column, string value);
    [DllImport("__Internal")]
    private static extern void IndexedDB_ClearList(string db, string store);
    [DllImport("__Internal")]
    private static extern void IndexedDB_SyncFS();
#endif

    private class Request
    {
        public string store;
        public string key;
        public string searchColumn;
        public object searchValue;
        public Action<string> stringCallback;
        public Action<string[]> listCallback;
        public Action<byte[]> bytesCallback;
    }
    public class Store
    {
        public string name;
        public string[] columns;
    }

    public bool isInited { get; private set; }
    private bool isIniting;

    private static Dictionary<string, bool> isLoadKvBusy = new Dictionary<string, bool>();
    private static Dictionary<string, bool> isLoadListBusy = new Dictionary<string, bool>();
    private static Dictionary<string, Queue<Request>> loadKvQueue = new Dictionary<string, Queue<Request>>();
    private static Dictionary<string, Queue<Request>> loadListQueue = new Dictionary<string, Queue<Request>>();

    // 实例回调存储
    private static Dictionary<string, Action> initCallback = new Dictionary<string, Action>();
    private static Dictionary<string, Action<string, byte[]>> kvCallback = new Dictionary<string, Action<string, byte[]>>();
    private static Dictionary<string, Action<string[]>> listCallback = new Dictionary<string, Action<string[]>>();

    // 初始化状态
    private static readonly Dictionary<string, StringCallback> _activeCallbacks = new();//仅用于确保回调方法的引用防止回收
    private string dbName;
    private int version;

    // 初始化数据库
    public IndexedDB(string dbName, int version)
    {
        this.dbName = dbName;
        this.version = version;
    }

    public void CreateStoreIfNotExist(Store[] stores)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            isIniting = true;
            initCallback[dbName] = () =>
            {
                isIniting = false;
                isInited = true;
            };
            JsonData storesArrJson = new JsonData();
            for (int i = 0; i < stores.Length; i++)
            {
                JsonData storeJson = new JsonData();
                storeJson["name"] = stores[i].name;
                storeJson["columns"] = new JsonData();
                foreach (var column in stores[i].columns)
                {
                    storeJson["columns"].Add(column);
                }
                storesArrJson.Add(storeJson);
            }
            string storesJson = storesArrJson.ToJson();
            IndexedDB_Init(dbName, version, storesJson, InitCallback);
        }
#endif
    }

    public void SetKeyValue(string key, string value)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_SetKeyValue(dbName, key, value, null, 0);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to SetKeyValue: " + e.Message);
        }
#endif
    }

    public void SetKeyValue(string key, byte[] value)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_SetKeyValue(dbName, key, null, value, 0);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to SetKeyValue: " + e.Message);
        }
#endif
    }

    public void GetValueByKey(string key, Action<string> callback)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        if (!isLoadKvBusy.ContainsKey(dbName))
        {
            isLoadKvBusy[dbName] = false;
        }
        if (isLoadKvBusy[dbName])
        {
            if (!loadKvQueue.ContainsKey(dbName))
            {
                loadKvQueue[dbName] = new Queue<Request>();
            }
            loadKvQueue[dbName].Enqueue(new Request()
            {
                key = key,
                stringCallback = callback,
            });
            return;
        }
        try
        {
            kvCallback[dbName] = (str, bytes) =>
            {
                callback?.Invoke(str);
            };
            isLoadKvBusy[dbName] = true;
            IndexedDB_GetKeyValue(dbName, key, OnStringLoaded, null);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to GetValueByKey: " + e.Message);
        }
#endif
    }

    public void GetValueByKey(string key, Action<byte[]> callback)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        if (!isLoadKvBusy.ContainsKey(dbName))
        {
            isLoadKvBusy[dbName] = false;
        }
        if (isLoadKvBusy[dbName])
        {
            if (!loadKvQueue.ContainsKey(dbName))
            {
                loadKvQueue[dbName] = new Queue<Request>();
            }
            loadKvQueue[dbName].Enqueue(new Request()
            {
                key = key,
                bytesCallback = callback,
            });
            return;
        }
        try
        {
            kvCallback[dbName] = (str, bytes) =>
            {
                callback?.Invoke(bytes);
            };
            isLoadKvBusy[dbName] = true;
            IndexedDB_GetKeyValue(dbName, key, null, OnBinaryLoaded);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to GetValueByKey: " + e.Message);
        }
#endif
    }

    public void DeleteKey(string key)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_DeleteKey(dbName, key);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to DeleteKey: " + e.Message);
        }
#endif
    }

    public void List_Insert(string store, string[] columns, object[] values)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_InsertToList(dbName, store, JsonMapper.ToJson(columns), JsonMapper.ToJson(values));
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_AddNew: " + e.Message);
        }
#endif
    }

    public void List_UpdateById(string store, int id, string[] columns, object[] values)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_UpdateListById(dbName, store, id, JsonMapper.ToJson(columns), JsonMapper.ToJson(values));
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_UpdateById: " + e.Message);
        }
#endif
    }

    /// <summary>
    /// 从列表读取字多个记录
    /// </summary>
    public void List_GetById(string store, int id, Action<string> callback)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        if (!isLoadListBusy.ContainsKey(dbName))
        {
            isLoadListBusy[dbName] = false;
        }
        if (isLoadListBusy[dbName])
        {
            if (!loadListQueue.ContainsKey(dbName))
            {
                loadListQueue[dbName] = new Queue<Request>();
            }
            loadListQueue[dbName].Enqueue(new Request()
            {
                store = store,
                searchColumn = "id",
                searchValue = id.ToString(),
                stringCallback = callback,
            });
            return;
        }
        try
        {
            listCallback[dbName] = strings =>
            {
                callback?.Invoke(strings != null && strings.Length > 0 ? strings[0] : null);
            };
            isLoadListBusy[dbName] = true;
            IndexedDB_SelectList(dbName, store, "id", id.ToString(), "int", OnListLoaded);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_GetById: " + e.Message);
            callback?.Invoke(null);
        }
#endif
    }
    
    /// <summary>
    /// 从列表读取字多个记录
    /// </summary>
    public void List_GetByColumn(string store, string column, object value, Action<string[]> callback)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        if (!isLoadListBusy.ContainsKey(dbName))
        {
            isLoadListBusy[dbName] = false;
        }
        if (isLoadListBusy[dbName])
        {
            if (!loadListQueue.ContainsKey(dbName))
            {
                loadListQueue[dbName] = new Queue<Request>();
            }
            loadListQueue[dbName].Enqueue(new Request()
            {
                store = store,
                searchColumn = column,
                searchValue = value,
                listCallback = callback,
            });
            return;
        }
        try
        {
            listCallback[dbName] = callback;
            isLoadListBusy[dbName] = true;
            if (!_activeCallbacks.ContainsKey(dbName))
            {
                _activeCallbacks.Add(dbName, OnListLoaded);
            }
            IndexedDB_SelectList(dbName, store, column, value.ToString(), value is string ? "string" : "int", _activeCallbacks[dbName]);
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_GetByCategory: " + e.Message);
            callback?.Invoke(null);
        }
#endif
    }

    public void List_DeleteById(string store, int id)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_DeleteListByColumn(dbName, store, "id", id.ToString());
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_DeleteById: " + e.Message);
        }
#endif
    }

    /// <summary>
    /// 从列表中根据类型删除记录
    /// </summary>
    public void List_DeleteByColumn(string store, string column, string value)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_DeleteListByColumn(dbName, store, column, value);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_DeleteByCategory: " + e.Message);
        }
#endif
    }

    /// <summary>
    /// 从列表中根据类型删除记录
    /// </summary>
    public void List_Clear(string store)
    {
#if UNITY_WEBGL
        if (!isInited)
        {
            Debug.LogError("未初始化");
            return;
        }
        try
        {
            IndexedDB_ClearList(dbName, store);
            IndexedDB_SyncFS();
        }
        catch (Exception e)
        {
            Debug.LogError("Failed to List_Clear: " + e.Message);
        }
#endif
    }

    #if UNITY_WEBGL
    [MonoPInvokeCallback(typeof(Callback))]
    private static void InitCallback(IntPtr dbNamePtr)
    {
        string dbName = ParseString(dbNamePtr);
        initCallback[dbName].Invoke();
    }

    [MonoPInvokeCallback(typeof(BinaryCallback))]
    private static void OnBinaryLoaded(IntPtr dbNamePtr, IntPtr dataPtr, int size)
    {
        string dbName = ParseString(dbNamePtr);
        byte[] result = null;
        if (dataPtr != IntPtr.Zero && size > 0)
        {
            try
            {
                result = new byte[size];
                Marshal.Copy(dataPtr, result, 0, size);
                // 释放内存
                Marshal.FreeHGlobal(dataPtr);
            }
            catch (Exception e)
            {
                Debug.LogError("Error copying binary data: " + e.Message);
            }
        }
        kvCallback[dbName](null, result);
        if (loadKvQueue.Count > 0)
        {
            Request next = loadKvQueue[dbName].Dequeue();
            bool isBinary = next.bytesCallback != null;
            kvCallback[dbName] = (str, bytes) =>
            {
                if (isBinary)
                {
                    next.bytesCallback(bytes);
                }
                else
                {
                    next.stringCallback(str);
                }
            };
            if (isBinary)
            {
                IndexedDB_GetKeyValue(dbName, next.key, null, OnBinaryLoaded);
            }
            else
            {
                IndexedDB_GetKeyValue(dbName, next.key, OnStringLoaded, null);
            }
        }
        else
        {
            kvCallback = null;
            isLoadKvBusy[dbName] = false;
        }
    }

    [MonoPInvokeCallback(typeof(StringCallback))]
    private static void OnStringLoaded(IntPtr dbNamePtr, IntPtr stringPtr)
    {
        string dbName = ParseString(dbNamePtr);
        string result = ParseString(stringPtr);
        kvCallback[dbName](result, null);
        if (loadKvQueue.Count > 0)
        {
            Request next = loadKvQueue[dbName].Dequeue();
            bool isBinary = next.bytesCallback != null;
            kvCallback[dbName] = (str, bytes) =>
            {
                if (isBinary)
                {
                    next.bytesCallback(bytes);
                }
                else
                {
                    next.stringCallback(str);
                }
            };
            if (isBinary)
            {
                IndexedDB_GetKeyValue(dbName, next.key, null, OnBinaryLoaded);
            }
            else
            {
                IndexedDB_GetKeyValue(dbName, next.key, OnStringLoaded, null);
            }
        }
        else
        {
            kvCallback = null;
            isLoadKvBusy[dbName] = false;
        }
    }
    
    [MonoPInvokeCallback(typeof(StringCallback))]
    private static void OnListLoaded(IntPtr dbNamePtr, IntPtr stringPtr)
    {
        string dbName = ParseString(dbNamePtr);
        string strListJson = stringPtr == IntPtr.Zero ? "[]" : ParseString(stringPtr);
        //Debug.Log($"strListJson : {strListJson}");
        JsonData strListJsonData = JsonMapper.ToObject(strListJson);
        List<string> strList = new List<string>();
        for (int i = 0; i < strListJsonData.Count; i++)
        {
            strList.Add(strListJsonData[i].ToJson());
        }
        listCallback[dbName](strList.ToArray());
        if (loadListQueue.Count > 0)
        {
            Request next = loadListQueue[dbName].Dequeue();
            listCallback[dbName] = next.listCallback;
            IndexedDB_SelectList(dbName, next.store, next.searchColumn, next.searchValue.ToString(), next.searchValue is string ? "string" : "int", OnListLoaded);
        }
        else
        {
            listCallback = null;
            isLoadListBusy[dbName] = false;
        }
    }

    private static string ParseString(IntPtr ptr)
    {
        try
        {
            if (ptr == IntPtr.Zero)
            {
                Debug.LogWarning("Attempted to parse null pointer");
                return string.Empty;
            }
            string result = Marshal.PtrToStringAnsi(ptr);
            // 如果字符串是通过Marshal.AllocHGlobal分配的，需要释放内存
            Marshal.FreeHGlobal(ptr);
            return result ?? string.Empty;
        }
        catch (Exception e)
        {
            Debug.LogError("Error parsing string: " + e.Message);
            return string.Empty;
        }
    }
#endif
}
